/**
 * @file: file_copy.c
 * @brief: 底层
 * @author: liaowx
 * @date: 2019.08.03
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/times.h>
#include <time.h>
#include <malloc.h>

#include "file_copy.h"
#include "core.h"

static size_t get_file_size(int fd);
static void *thread_copy_fn(void *arg);

int normal_copy(char *file_name)
{
    /* 用于判断是否读取结束的 */
    int size = 0, clocks_per_sec;
    /* 字符类型只有1字节，可以将各个类型的拿出来 */
    char buf[FILE_SIZE] = {0};
    struct tms st_tms;
    int p_src, p_dest;
    clock_t start, end;

    /* 打开第一个文件，即已存在的文件 */
    p_src = open(file_name, O_RDONLY);
    if (!p_src) {
        printf("can't open src_file\n");
        return 0;
    }

    /* 打开第二个文件，不存在的文件 */
    p_dest = open(FILE_DEST, O_CREAT|O_WRONLY, 0644);
    if (!p_dest) {
        printf("can't open dest_file\n");
        close(p_src);
        return 0;
    }
    /* 记录起始时间 */
    start = clock();
    while (1) {
        size = read(p_src, buf, sizeof(buf));
        write(p_dest, buf, size);
        if (!size) {
            break;
        }
    }

    if (times(&st_tms) == -1) {
        printf("times error\n");
        return -1;
    }
    end = clock();
    /* 用于获得times函数的单位时钟数 */
	clocks_per_sec = sysconf(_SC_CLK_TCK);
	printf("syst  times: %7dus\n", st_tms.tms_stime*1000000/(clocks_per_sec));

    close(p_src);
    close(p_dest);

    return 0;
}

int optimize_copy(char *file_name)
{
    int infd = open(file_name, O_RDONLY);
    int outfd = open(FILE_DEST, O_CREAT|O_WRONLY, 0644);
    size_t file_size = get_file_size(infd);
    size_t thread_size = THREADS_COUNT;
    struct thread_block *blocks = (struct thread_block *)
                                    malloc(sizeof(struct thread_block )* thread_size);
    size_t percent = file_size / thread_size;
    int i = 0, clocks_per_sec;
    struct tms st_tms;
    clock_t start, end;

    /* 记录起始时间 */
    start = clock();
    if (infd == -1 || -1 == outfd) {
        printf("error while open file \n");
        return -1;
    }

    /* init-thread-block */
    for (; i < thread_size; ++i) {
        blocks[i].infd = infd;
        blocks[i].outfd = outfd;
        blocks[i].start = i * percent;
        blocks[i].end = blocks[i].start + percent;
    }

    /* the last thread */
    blocks[i].end = file_size;
    pthread_t ptid[thread_size];

    /* 创建线程 */
    for (i = 0 ; i < thread_size; ++i) {
        pthread_create(&ptid[i], NULL, thread_copy_fn, &(blocks[i]));
    }

    /* 加入线程 */
    for (i = 0 ; i < thread_size; ++i) {
        pthread_join(ptid[i], NULL);
    }

    if (times(&st_tms) == -1) {
        printf("times error\n");
        return -1;
    }

    end = clock();
    /* 用于获得times函数的单位时钟数 */
	clocks_per_sec = sysconf(_SC_CLK_TCK);
	printf("syst  times: %7dus\n", st_tms.tms_stime*1000000/(clocks_per_sec));

    /* 释放资源 */
    free(blocks);
    close(infd);
    close(outfd);
    printf("Copy Successfully \n");

    return 0;
}

static size_t get_file_size(int fd)
{
    struct stat st;
    fstat(fd, &st);
    return st.st_size;
}

static void *thread_copy_fn(void *arg)
{
    struct thread_block *block = (struct thread_block *)arg;
    char buf[THREADS_BUFF_SIZE];
    size_t count = block->start;
    char *ptr_write;
    int ret;
    int bytes_read;
    int bytes_write;

    /* lseek到同样的位置 */
    ret = lseek(block->infd, block->start, SEEK_SET);
    ret = lseek(block->outfd, block->start, SEEK_SET);

    while (count < block->end) {
        bytes_read = read(block->infd, buf, sizeof(buf));
        if (bytes_read > 0) {
            count += bytes_read;

            /* read（）返回-1，同时errno为EINTR，表示读的过程中遇到了中断 */
            if ((bytes_read == -1) && (errno != EINTR))
                    break;
            ptr_write = buf;
            while ((bytes_write = write(block->outfd, ptr_write, bytes_read)) != 0) {
                /* write（）会返回-1，同时errno为EINTR，表示在写的过程中遇到了中断 */
                if ((bytes_write == -1) && (errno!= EINTR))
                    break;
                if (bytes_write == bytes_read)
                    break;
                if (bytes_write > 0) {
                    ptr_write += bytes_write;
                    bytes_read -= bytes_write;
                }
                printf("thread = %ld\t write = %d\t read %d\n",\
                    pthread_self(), bytes_write, bytes_read);
            }

            if (bytes_write == -1)
                break;
        }
    }
    pthread_exit(NULL); 
}

struct core *get_dev(void)
{
    struct device *file_copy[CMD_NUM];
    static struct core core[CMD_NUM];

    file_copy[NORMAL] = (struct device *)malloc(sizeof(struct device));
    file_copy[OPTIMIZE] = (struct device *)malloc(sizeof(struct device));

    file_copy[NORMAL]->cmd[NORMAL] = "normal\0";
    file_copy[OPTIMIZE]->cmd[OPTIMIZE] = "optimize\0";

    core[NORMAL].arg[NORMAL] = file_copy[NORMAL]->cmd[NORMAL];
    core[NORMAL].arg[OPTIMIZE] = file_copy[OPTIMIZE]->cmd[OPTIMIZE];
    core[NORMAL].copy_ctl = normal_copy;

    core[OPTIMIZE].arg[NORMAL] = file_copy[NORMAL]->cmd[NORMAL];
    core[OPTIMIZE].arg[OPTIMIZE] = file_copy[OPTIMIZE]->cmd[OPTIMIZE];
    core[OPTIMIZE].copy_ctl = optimize_copy;

    return core;
}